import { Express, Request, Response } from 'express';
import { MongoClient } from 'mongodb';
import { generateGlob, verifySalt } from '../util/strutil';
import { User } from '../models/user';
import { TokenPool } from '../objects/TokenPool';
import { ServerConfig } from '../objects/ServerConfig';
import cryptoRandomString from 'crypto-random-string';
import { Collections } from '../objects/Collections';

export class AuthRoute {
	constructor(app: Express, config: ServerConfig, mongo: MongoClient, pool: TokenPool) {
		this.mongo = mongo;
		this.pool = pool;
		this.config = config;
		app.post('/login', this.login.bind(this));
		app.post('/logout', this.logout.bind(this));
		app.post('/register', this.register.bind(this));
	}

	private login(req: Request, res: Response) {
		let username = req.body['username'];
		let password = req.body['password'];
		if (username == null || password == null) {
			res.status(400);
			res.end();
			return;
		}
		this.mongo.db().collection(Collections.USERS).find({ username: username }).toArray().then(arr => {
			if (arr.length > 0) {
				let user = arr[0] as User;
				if (verifySalt(password, user.salt, user.password)) {
					this.pool.dispenseToken(user.username).then(token => {
						res.send(token);
					})
				} else {
					res.status(403);
					res.end();
				}
			} else {
				res.status(404);
				res.end();
			}
		});
	}

	private logout(req: Request, res: Response) {
		let token = req.body['token'];
		if (token != null) {
			this.pool.checkToken(req, res, () => {
				this.pool.revokeToken(token).then(() => {
					res.send({
						code: 0,
						revokedToken: token
					});
				}).catch(e => {
					res.status(500);
					res.send(e);
				});
			}).catch(e => {
				res.status(500);
				res.send(e);
			});
		} else {
			res.status(400);
		}
	}

	private async register(req: Request, res: Response) {
		if (this.config.allowRegistration) {
			const username = req.body['username'];
			const password = req.body['password'];
			const salt = cryptoRandomString({ length: 16 });
			const glob = generateGlob(password, salt);
			const user = {
				username: username,
				password: glob,
				salt: salt
			};
			const collection = this.mongo.db().collection<User>(Collections.USERS);
			let count = await collection.find({ username: username }).count();
			if (count > 0) {
				res.send({
					code: 1,
					reason: 'username taken'
				});
			} else {
				await collection.insertOne(user);
				res.send({
					code: 0
				});
			}
		} else {
			res.status(403);
			res.send({
				reason: 'registration not allowed'
			});
		}
	}

	private mongo: MongoClient;
	private config: ServerConfig;
	private pool: TokenPool;
}
